package com.sqbang.dbcompare.util;

import com.sqbang.dbcompare.constant.CommonConst;
import com.sqbang.dbcompare.constant.SystemConstant;
import com.sqbang.dbcompare.constant.Whether;
import com.sqbang.dbcompare.pojo.cache.CommonData;
import com.sqbang.dbcompare.pojo.dto.DatabaseInfoDto;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 工具类
 *
 * @author suqiongbang
 * @date 2021/10/22 21:41
 */
public class Tools {

    /**
     * 数据库连接 缓存map
     */
    private final static Map<Integer, Connection> CONNECTION_CACHE_MAP = new HashMap<>();

    /**
     * mysql保留字
     */
    private final static String MYSQL_RESERVED_WORDS = "accessible,add,all,alter,analyze,and,as,asc,asensitive,before,between,bigint,binary,blob,both,by,call,cascade,case,change,char,character,check,collate,column,condition,constraint,continue,convert,create,cross,current_date,current_time,current_timestamp,current_user,cursor,database,databases,day_hour,day_microsecond,day_minute,day_second,dec,decimal,declare,default,delayed,delete,desc,describe,deterministic,distinct,distinctrow,div,double,drop,dual,each,else,elseif,enclosed,escaped,exists,exit,explain,false,fetch,float,float4,float8,for,force,foreign,from,fulltext,generated,get,grant,group,having,high_priority,hour_microsecond,hour_minute,hour_second,if,ignore,in,index,infile,inner,inout,insensitive,insert,int,int1,int2,int3,int4,int8,integer,interval,into,io_after_gtids,io_before_gtids,is,iterate,join,key,keys,kill,leading,leave,left,like,limit,linear,lines,load,localtime,localtimestamp,lock,long,longblob,longtext,loop,low_priority,master_bind,master_ssl_verify_server_cert,match,maxvalue,mediumblob,mediumint,mediumtext,middleint,minute_microsecond,minute_second,mod,modifies,natural,not,no_write_to_binlog,null,numeric,on,optimize,optimizer_costs,option,optionally,or,order,out,outer,outfile,partition,precision,primary,procedure,purge,range,read,reads,read_write,real,references,regexp,release,rename,repeat,replace,require,resignal,restrict,return,revoke,right,rlike,schema,schemas,second_microsecond,select,sensitive,separator,set,show,signal,smallint,spatial,specific,sql,sqlexception,sqlstate,sqlwarning,sql_big_result,sql_calc_found_rows,sql_small_result,ssl,starting,stored,straight_join,table,terminated,then,tinyblob,tinyint,tinytext,to,trailing,trigger,true,undo,union,unique,unlock,unsigned,update,usage,use,using,utc_date,utc_time,utc_timestamp,values,varbinary,varchar,varcharacter,varying,virtual,when,where,while,with,write,xor,year_month,zerofill";

    /**
     * 缓存内存数据到文件
     *
     * @param fileName
     */
    public static void setCacheData(String fileName, Object cacheData) {
        String dataDirPath = System.getProperty("user.dir") + File.separator + SystemConstant.FilePath.DATA_DISK_DIR;
        Path dirPath = Paths.get(dataDirPath);
        Path fileDataPath = Paths.get(dirPath + File.separator + fileName);
        try {
            if (!Files.exists(dirPath)) {
                Files.createDirectories(dirPath);
            }
            // 将对象存储在对象输出流中
            if (!Files.exists(fileDataPath)) {
                Files.createFile(fileDataPath);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 将内存写入文件
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileDataPath.toString()))) {
            out.writeObject(cacheData);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析文件数据到内存
     *
     * @param fileName
     */
    public static Object getCacheData(String fileName) {
        // SystemConstant.FilePath.databaseInfoDataFileName
        // CommonData.databaseInfoList
        Path fileDataPath = getPath(fileName);
        if (Files.exists(fileDataPath)) {
            try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileDataPath.toString()))) {
                return in.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static Path getPath(String fileName) {
        String dataDirPath = System.getProperty("user.dir") + File.separator + SystemConstant.FilePath.DATA_DISK_DIR;
        Path dirPath = Paths.get(dataDirPath);
        return Paths.get(dirPath + File.separator + fileName);
    }

    /**
     * 使用正则表达式来判断字符串中是否包含大写字母
     *
     * @param str 待检验的字符串
     * @return 返回是否包含
     * true: 包含大写字母 ;false 不包含大写字母
     */
    public static boolean isContainUpperCase(String str) {
//        String regex=".*[a-zA-Z]+.*";
        String regex = ".*[A-Z]+.*";
        Matcher m = Pattern.compile(regex).matcher(str);
        return m.matches();
    }

    /**
     * 判断字符串是不是以数字开头
     *
     * @param str
     * @return
     */
    public static boolean isStartWithNumber(String str) {
        String regex = "[0-9].*";
        Matcher m = Pattern.compile(regex).matcher(str.charAt(0) + "");
        return m.matches();
    }

    /**
     * 判断是否是保留字
     *
     * @param wordOfLowerCase 小写字母的单词
     * @return
     */
    public static boolean isReservedWord(String wordOfLowerCase) {
        if (MYSQL_RESERVED_WORDS.contains(wordOfLowerCase)) {
            return true;
        }
        return false;
    }

    /**
     * 根据配置的ID，获取数据库连接实例
     *
     * @param key
     * @return
     */
    public static Connection getConnection(Integer key) {
        Connection connection = CONNECTION_CACHE_MAP.get(key);
        if (null != connection) {
            try {
                // 测试连接有效性
                if (!connection.isValid(3000)) {
                    connection.close();
                    CONNECTION_CACHE_MAP.put(key, buildConnection(key));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return connection;
        } else {
            CONNECTION_CACHE_MAP.put(key, buildConnection(key));
            return CONNECTION_CACHE_MAP.get(key);
        }
    }

    /**
     * 移除数据库连接实例
     *
     * @param key
     */
    public static void removeConnection(Integer key) {
        Connection connection = CONNECTION_CACHE_MAP.get(key);
        if (null != connection) {
            CONNECTION_CACHE_MAP.remove(key);
        }
    }

    /**
     * 构建数据库连接实例
     *
     * @param key
     * @return
     */
    private static Connection buildConnection(Integer key) {
        DatabaseInfoDto databaseInfoDto = CommonData.databaseInfoList.get(key.intValue());
        Connection conn = null;
        try {
            Class.forName(CommonConst.DRIVER_CLASS_NAME);
            String url = CommonConst.URL_PREFIX + databaseInfoDto.getHost() + CommonConst.SYMBOL_COLON
                    + databaseInfoDto.getPort() + CommonConst.SYMBOL_SLANTING_BAR + databaseInfoDto.getDatabase() + CommonConst.URL_SUFFIX;
            conn = DriverManager.getConnection(url, databaseInfoDto.getUserName(), databaseInfoDto.getPassword());
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 从inputStream 里面读取字符串内容
     * @param in
     * @return
     * @throws IOException
     */
    public static String getStringFromInputString(InputStream in) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder out = new StringBuilder();
        String newLine = System.getProperty("line.separator");
        String line;
        while ((line = reader.readLine()) != null) {
            out.append(line);
            out.append(newLine);
        }
        return out.toString();
    }

    /**
     * 带下划线的字段转为属性名
     * @param underlineName
     * @param isFirstUpperCase
     * @return
     */
    public static String underline2PropertyName(String underlineName, Integer isFirstUpperCase, String ignoreTablePrefix) {
        if (underlineName == null || underlineName.length() == 0) {
            return "";
        }
        if (ignoreTablePrefix != null && ignoreTablePrefix.length() > 0) {
            if (ignoreTablePrefix.contains(",")) {
                String[] ignoreTablePrefixArr = ignoreTablePrefix.split(",");
                for (String ignoreTablePre : ignoreTablePrefixArr) {
                    if (underlineName.indexOf(ignoreTablePre) == 0){
                        underlineName = underlineName.substring(ignoreTablePre.length());
                        break;
                    }
                }
            } else if (underlineName.indexOf(ignoreTablePrefix) == 0){
                underlineName = underlineName.substring(ignoreTablePrefix.length());
            }
        }
        if (!underlineName.contains("_")) {
            return Whether.NO.equals(isFirstUpperCase) ? underlineName : underlineName.substring(0,1).toUpperCase(Locale.ROOT) + underlineName.substring(1);
        }
        // 去掉头尾的下画线
        while (underlineName.indexOf("_") == 0) {
            underlineName = underlineName.substring(1);
        }
        while (underlineName.lastIndexOf("_") == underlineName.length() - 1) {
            underlineName = underlineName.substring(0, underlineName.length() - 1);
        }
        while (underlineName.contains("__")) {
            underlineName = underlineName.replaceAll("__", "_");
        }
        String[] fragWordArr = underlineName.split("_");
        if (fragWordArr.length > 1) {
            underlineName = fragWordArr[0];
            underlineName = Whether.NO.equals(isFirstUpperCase) ? underlineName : underlineName.substring(0,1).toUpperCase(Locale.ROOT) + underlineName.substring(1);
            for (int i = 1; i < fragWordArr.length; i++) {
                underlineName += fragWordArr[i].substring(0,1).toUpperCase(Locale.ROOT) + fragWordArr[i].substring(1);
            }
            return underlineName;
        } else {
            underlineName = fragWordArr[0];
            return Whether.NO.equals(isFirstUpperCase) ? underlineName : underlineName.substring(0,1).toUpperCase(Locale.ROOT) + underlineName.substring(1);
        }
    }
}
